package com.touchfired.common.context;

import com.touchfired.common.constants.ClientInfo;
import com.touchfired.common.constants.EventCode;
import com.touchfired.common.constants.RoomInfo;
import com.touchfired.common.exception.ErrorCode;
import com.touchfired.common.exception.ServiceException;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 服务器上下文
 *
 * @author 幸佳俊
 */
@Component
public class ServerContext {

    // ----------------------------------客户端-----------------------------------

    /** 客户端信息 */
    private static final Map<String, ClientInfo> clientInfoMap = new ConcurrentHashMap<>();

    /**
     * 客户端连接后登录
     * @param clientInfo 客户端信息
     */
    public void addClientInfo(ClientInfo clientInfo) {
        if (clientInfo != null) {
            String longId = clientInfo.getLongId();
            Channel channel = clientInfo.getChannel();
            if (StringUtils.isBlank(longId) || channel == null) {
                throw new ServiceException(ErrorCode.CONNECTED_ADD_CLIENT_INFO_ERROR);
            }
            if (!clientInfoMap.containsKey(longId)) {
                clientInfoMap.put(longId, clientInfo);
            }
        }
    }

    /**
     * 获取客户端信息
     * @param longId 客户端通道id
     * @return 客户端信息
     */
    public ClientInfo getClientInfo(String longId) {
        return clientInfoMap.get(longId);
    }

    /**
     * 更新客户端信息
     * @param clientInfo 客户端信息
     */
    public void updateClientInfo(ClientInfo clientInfo) {
        if (clientInfo != null) {
            String longId = clientInfo.getLongId();
            if (StringUtils.isNotBlank(longId)) {
                ClientInfo originClientInfo = clientInfoMap.get(longId);
                if (originClientInfo != null) {
                    clientInfoMap.put(longId, clientInfo);
                }
            }
        }
    }

    // ----------------------------------房间----------------------------------

    /** 房间信息 */
    private static final Map<String, RoomInfo> roomInfoMap = new ConcurrentHashMap<>();

    /** 房间id计数器 */
    private static final AtomicLong roomIdCounter = new AtomicLong(0);

    /**
     * 创建房间
     * @param roomInfo 房间信息
     * @return 房间id
     */
    public void addRoomInfo(RoomInfo roomInfo) {
        if (roomInfo == null) {
            throw new ServiceException(ErrorCode.ROOM_CREATE_PARAMS_NULL_ERROR);
        }
        String roomId = String.valueOf(roomIdCounter.incrementAndGet());
        roomInfo.setRoomId(roomId);
        roomInfoMap.put(roomId, roomInfo);
    }

    /**
     * 更新房间信息
     * @param roomInfo 新的房间信息
     */
    public void updateRoomInfo(RoomInfo roomInfo) {
        if (roomInfo != null) {
            String roomId = roomInfo.getRoomId();
            if (StringUtils.isNotBlank(roomId)) {
                RoomInfo originRoomInfo = roomInfoMap.get(roomId);
                if (originRoomInfo != null) {
                    roomInfoMap.put(roomId, roomInfo);
                }
            }
        }
    }










    /** 预连接客户端 */
    private static final Map<String, ClientInfo> preConnectClientMap = new ConcurrentHashMap<>();

    /** 全局用户组 */
    private static final ChannelGroup globalChannelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 预连接客户端
     * @param host 客户端地址
     * @param name 客户端名称
     */
    public void preConnect(String host, String name) {
        if (StringUtils.isNotBlank(host)) {
            ClientInfo preClientInfo = new ClientInfo().setName(name).setHost(host);
            preClientInfo.appendEventNode(EventCode.CLIENT_CONNECTING);
            preConnectClientMap.put(host, preClientInfo);
        } else {
            throw new ServiceException(ErrorCode.ERROR_500);
        }
    }

    /**
     * 获取预连接客户端信息
     * @param host 客户端地址
     * @return 客户端信息
     */
    public ClientInfo getPreConnectClient(String host) {
        if (StringUtils.isNotBlank(host)) {
            return preConnectClientMap.get(host);
        }
        return null;
    }

    /**
     * 连接客户端
     * @param channel 客户端通道
     */
    public ClientInfo connected(Channel channel) {
        String remoteAddress = channel.remoteAddress().toString();
        String host = remoteAddress.substring(1, remoteAddress.lastIndexOf(":"));
        ClientInfo clientInfo = getPreConnectClient(host);
        if (clientInfo != null) {
            String clientLongId = channel.id().asLongText();
            if (!clientInfoMap.containsKey(clientLongId)) {
                clientInfo.setLongId(clientLongId)
                        .setChannel(channel);
                clientInfo.appendEventNode(EventCode.CLIENT_CONNECTED);
                clientInfoMap.put(clientLongId, clientInfo);
            }
        }
        return clientInfo;
    }

    /**
     * 移除用户
     * @param channel 客户端通道
     */
    public void removeClientInfo(Channel channel) {
        if (channel != null) {
            globalChannelGroup.remove(channel);
            clientInfoMap.remove(channel.id().asLongText());
        }
    }

    /**
     * 根据房间id获取房间信息
     * @param roomId 房间id
     * @return 房间信息
     */
    public RoomInfo getRoomInfo(String roomId) {
        if (StringUtils.isNotBlank(roomId)) {
            return roomInfoMap.get(roomId);
        }
        return null;
    }

    /**
     * 移除房间
     * @param roomId 房间id
     */
    public void removeRoomInfo(String roomId) {
        if (StringUtils.isNotBlank(roomId)) {
            roomInfoMap.remove(roomId);
        }
    }

    /**
     * 获取房间列表
     * @return 房间列表
     */
    public List<RoomInfo> getRooms() {
        List<RoomInfo> result = new ArrayList<>();
        for (Map.Entry<String, RoomInfo> roomInfoEntry : roomInfoMap.entrySet()) {
            result.add(roomInfoEntry.getValue());
        }
        return result;
    }
}
